#ifndef LINUX_SYSCALL_H
#define LINUX_SYSCALL_H

#include <types.h>
#include <os.h>

#define SYSCALL_PMC         5

#define SYS_NR_SLEEP        1
#define SYS_NR_GETPID       2
#define SYS_NR_PRINTS       3
#define SYS_NR_FORK         4
#define SYS_NR_EXECVE       5
#define SYS_NR_YIELD        6
#define SYS_NR_OPEN         50
#define SYS_NR_READ         51
#define SYS_NR_WRITE        52
#define SYS_NR_CLOSE        53
#define SYS_NR_LSEEK        54

#pragma GCC diagnostic ignored "-Wbuiltin-declaration-mismatch"

void syscall_handler();

typedef struct syscall_arg_t
{
    // 系统调用号
    uint32_t id;
    // 参数
    int arg0, arg1, arg2, arg3;
} syscall_arg_t;

typedef struct syscall_frame_t
{
    // 手动压入
    uint32_t eflags;
    uint32_t gs, fs, es, ds;
    uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
    // 自动压入
    uint32_t eip, cs;
    uint32_t id, arg0, arg1, arg2, arg3;
    uint32_t _esp, ss;
} syscall_frame_t;

static inline uint32_t _syscall(syscall_arg_t *arg)
{
    uint32_t addr[] = { 0, SYSCALL_GATE_SEG | 0 };
    uint32_t ret;
    __asm__ volatile("push %[arg3];"
            "push %[arg2];"
            "push %[arg1];"
            "push %[arg0];"
            "push %[id];"
            "lcalll *(%[a]);"
            :"=a"(ret)
            :[arg3]"r"(arg->arg3), [arg2]"r"(arg->arg2), [arg1]"r"(arg->arg1),
            [arg0]"r"(arg->arg0), [id]"r"(arg->id), [a]"r"(addr));
    return ret;
}

static inline void sleep(uint32_t ms)
{
    syscall_arg_t sleep_arg = { SYS_NR_SLEEP, ms, 0, 0, 0 };
    _syscall(&sleep_arg);
}

static inline uint32_t getpid()
{
    syscall_arg_t getpid_arg = { SYS_NR_GETPID, 0, 0, 0, 0 };
    return _syscall(&getpid_arg);
}

static inline void prints(const char *fmt, int arg)
{
    syscall_arg_t printf_arg = { SYS_NR_PRINTS, (uint32_t)fmt, arg, 0, 0 };
    _syscall(&printf_arg);
}

static inline int fork()
{
    syscall_arg_t fork_arg = { SYS_NR_FORK, 0, 0, 0, 0 };
    return _syscall(&fork_arg);
}

static inline int yield()
{
    syscall_arg_t fork_arg = { SYS_NR_YIELD, 0, 0, 0, 0 };
    return _syscall(&fork_arg);
}

static inline int execve(const char *name, char *const *argv, char *const *env)
{
    syscall_arg_t exec_arg = { SYS_NR_EXECVE, (int)name, (int)argv, (int)env, 0 };
    _syscall(&exec_arg);
}

int open(const char *name, int flag, ...);
int read(int file, char *str, int len);
int write(int file, char *str, int len);
int close(int file);
int lseek(int file, int offset, int start);


#endif